package zydhlog

import (
	"github.com/spf13/viper"
	"os"
	"runtime"
	"strconv"
)

/**
日志库
接口，日志可以输出到终端，也可以输出到文件，输出到kafka

需求分析
	1.可以不同地方写入
	2.日志分级别
			debug
			trace
			info
			warning
			error
			fatal
	3.日志要支持开关控制,比如开发的时候什么级别都能输出，但是上线后只有info级别的往下的才能输出
	4.日志内容，要有时间，行号，文件名，日志级别，日志信息
	5.日志文件要切割


	author : zydh
	.dev : 把日志写入文件 - no goroutine
		5.24 update:通过 goroutine + channel 写日志
*/

// 日志等级
const (
	ERR_LEVEL_NO int = iota
	ERR_LEVEL_DEBUG
	ERR_LEVEL_TRACE
	ERR_LEVEL_INFO
	ERR_LEVEL_WARNING
	ERR_LEVEL_ERROR
	ERR_LEVEL_FATAL
)

var (
	//#调试等级，关闭打开 0 关闭，不写入 1 打开
	debugMode uint
	//#打印位置 0 终端，1 文件 2 通道->文件
	logToSite uint
	//#日志存放路径
	logFilePath string
	//#当用通道写入文件时，设置多少个内核运行  runtime.GOMAXPROCS(?)
	runtimeGomaxprocs int
	//#日志通道能放的最大队列数
	logChanMaxCap uint
	//#分隔文件的方式 -1 不分割 0 按照文件大小切割 1 按照时间日期切割
	cutFileType int
	//#每条日志显示的时间格式
	logShowTimeFormat string
	//#按日期切割文件的名称时间格式
	cutFileByDateTimeFormat string
	//#按文件大小切割文件的名称时间格式
	cutFileBySizeTimeFormat string
	//#文件超过多大时，切割文件  1 * 1024 * 1024  //1m
	logFileMaxSize int
	//#日志钉锚的层级  runtime.Caller(layer)
	runtimeCallerLayer int
	//打印等级 info  warning  error  fatal
	//log_level = 0  不打印
	//log_level = 1  打印 fatal
	//log_level = 2  打印 error  fatal
	//log_level = 3  打印 warning  error  fatal
	//log_level = 4  打印所有 info  warning  error  fatal
	logLevel int
)

// 日志存放路径
var (
	//LOG_FILE_PATH_DEBUG   string = "./zydhlog/debug.txt"   //debug日志路径 debug?什么内容？不明
	//LOG_FILE_PATH_TRACE   string = "./zydhlog/trace.txt"   //trace日志路径 trace?什么内容？不明
	logFilePathInfo    string = "info.txt"    //普通日志路径
	logFilePathWarning string = "warning.txt" //警告日志路径
	logFilePathError   string = "error.txt"   //错误日志路径
	logFilePathFatal   string = "fatal.txt"   //500服务器内部致命错误日志路径
)

// LogChan 日志通道
var logChan chan *logToChan

// LogToFileByGoChan 写入通道的日志格式
type logToChan struct {
	leveL   int    //错误级别
	logPath string //日志存放位置
	logMsg  string //错误的信息
}

// NewLogChan 初始化
func newLogChan(level int, logPath, logMsg string) *logToChan {
	return &logToChan{
		leveL:   level,
		logPath: logPath,
		logMsg:  logMsg,
	}
}

// InitZydhLog 初始化日志
func InitZydhLog() {
	setLogParam()
}

// SetLogParam 设置log参数 - 改ini struct为 viper
//func SetLogParam_old(DebugMode, LogToSite, LogChanMaxCap uint, LogFilePath, LogShowTimeFormat, CutFileByDateTimeFormat, CutFileBySizeTimeFormat string, LogLevel, CutFileType, LogFileMaxSize, RuntimeCallerLayer, RuntimeGomaxprocs int) {
//	debugMode = DebugMode
//	logToSite = LogToSite
//	logFilePath = LogFilePath
//	runtimeGomaxprocs = RuntimeGomaxprocs
//	logChanMaxCap = LogChanMaxCap
//	logShowTimeFormat = LogShowTimeFormat
//	cutFileByDateTimeFormat = CutFileByDateTimeFormat
//	cutFileBySizeTimeFormat = CutFileBySizeTimeFormat
//	logLevel = LogLevel
//	cutFileType = CutFileType
//	logFileMaxSize = LogFileMaxSize
//	runtimeCallerLayer = RuntimeCallerLayer
//
//	//设置日志通道
//	if debugMode == 1 && logToSite == 2 {
//		setLogChan()
//	}
//}

func setLogParam() {
	debugMode = AtoUInt[uint](viper.Get("zydhlog.debug_mode").(string))
	logToSite = AtoUInt[uint](viper.Get("zydhlog.log_to_site").(string))
	logFilePath = viper.Get("zydhlog.log_file_path").(string)
	runtimeGomaxprocs = AtoUInt[int](viper.Get("zydhlog.runtime_gomaxprocs").(string))

	logChanMaxCap = AtoUInt[uint](viper.Get("zydhlog.log_chan_max_cap").(string))
	logShowTimeFormat = viper.Get("zydhlog.log_show_time_format").(string)
	cutFileByDateTimeFormat = viper.Get("zydhlog.cut_file_by_date_time_format").(string)
	cutFileBySizeTimeFormat = viper.Get("zydhlog.cut_file_by_size_time_format").(string)
	logLevel = AtoUInt[int](viper.Get("zydhlog.log_level").(string))
	cutFileType = AtoUInt[int](viper.Get("zydhlog.cut_file_type").(string))
	logFileMaxSize = AtoUInt[int](viper.Get("zydhlog.log_file_max_size").(string))
	runtimeCallerLayer = AtoUInt[int](viper.Get("zydhlog.runtime_caller_layer").(string))

	//设置日志通道
	if debugMode == 1 && logToSite == 2 {
		setLogChan()
	}
}

// AtoUInt 泛型，根据所传T类型，返回对应的类型
func AtoUInt[T int | uint](tempString string) T {
	intNum, _ := strconv.Atoi(tempString)
	return T(intNum)
}

// setLogChan 设置日志通道 //当开启日志 且 是使用go-chan方法时，初始化通道
// 要使用通道，程序需要一直运行着 gin.r.run()  http.request("/"),funs(c *gin Content){  logChan <- log   //写入日志 }
func setLogChan() {
	logChan = make(chan *logToChan, logChanMaxCap) //初始化通道
	runtime.GOMAXPROCS(runtimeGomaxprocs)
	//开启通道处理
	for i := 0; i <= 3; i++ {
		go getChanLogToFile(logChan)
	}
}

//调用方法
//根据 打印等级  往终端写入
//funs Trace(logInfo string, arg ...any) {
//	if logLevel > 0 {
//		logWriteToScene(ERR_LEVEL_TRACE, LOG_FILE_PATH_TRACE, logInfo, arg)
//	}
//}
//
//funs Debug(logInfo string, arg ...any) {
//	if logLevel > 0 {
//		logWriteToScene(ERR_LEVEL_DEBUG, LOG_FILE_PATH_DEBUG, logInfo, arg)
//	}
//}

func Info(logInfo string, arg ...any) {
	if logLevel > 3 {
		logWriteToScene(ERR_LEVEL_INFO, logFilePathInfo, logInfo, arg)
	}
}

func Warning(logInfo string, arg ...any) {
	if logLevel > 2 {
		logWriteToScene(ERR_LEVEL_WARNING, logFilePathWarning, logInfo, arg)
	}
}

func Error(logInfo string, arg ...any) {
	if logLevel > 1 {
		logWriteToScene(ERR_LEVEL_ERROR, logFilePathError, logInfo, arg)
	}
}

func Fatal(logInfo string, arg ...any) {
	if logLevel > 0 {
		logWriteToScene(ERR_LEVEL_FATAL, logFilePathFatal, logInfo, arg)
		os.Exit(1)
	}
}
